
#include <cassert>
#include <iostream>
#include <cstdlib>

using namespace std;
typedef long double inFloat;

class Complex{

 private: 			
	inFloat real;
	inFloat imaginario;

 public:
	Complex();
	Complex(const inFloat&, const inFloat&);
	Complex(const Complex&);
	

	Complex& suma(const Complex&); // a este complejo this le sumo el del parámetro y también resta 
	Complex& producto(const Complex&);
	Complex& producto(const inFloat&); // producto x un lambda - REQUIERE el friend operator* en inFloat.h 
	inFloat moduloModulo() const; //Sería el módulo | |  al cuadrado (para evitar hacer raíz sobre el infloat)
	inFloat Re() const; // proyector de la componente real
	inFloat Im() const; // id. de la componenete imaginaria
	Complex& cuadrado(); //tal vez hace menos operaciones, pero no sé... 

	//setters
	Complex& setRe(inFloat);
	Complex& setIm(inFloat);	

	friend Complex operator+(const Complex&, const Complex&); // REQUEIRE: friend operator+ definido en inFloat
	friend Complex operator-(const Complex&, const Complex&); // REQUEIRE: friend operator- definido en inFloat
	friend Complex operator*(const Complex&, const Complex&); // REQUIERE: friend operator* definido en inFloat
	friend Complex operator*(const inFloat&, const Complex&); // Escalar! requiere lo de arriba	
	bool operator==(const Complex&) const; 
	bool operator!=(const Complex&) const;	
	Complex& operator=(const Complex&);
};


Complex::Complex(){
}

Complex::Complex(const inFloat& re, const inFloat& im){
	this->real = re;
	this->imaginario = im;
}

Complex::Complex(const Complex& z){
	this->real = z.Re();
	this->imaginario = z.Im();
}

Complex operator+(const Complex& z, const Complex& w){
	return Complex( z.Re()+w.Re(), z.Im()+w.Im() );
}

Complex operator-(const Complex& z, const Complex& w){
	return Complex( z.Re()-w.Re(), z.Im()-w.Im() );
}

Complex operator*(const Complex& z, const Complex& w){
	return Complex( (z.Re()*w.Re() - z.Im()*w.Im()) , (w.Re()*z.Im() + w.Im()*z.Re()) );
}

Complex operator*(const inFloat& lambda, const Complex& z){
	return Complex( z.Re() * lambda, z.Im() * lambda);
}

inFloat Complex::Re() const {
	return this->real;
}


inFloat Complex::Im() const {
	return this->imaginario;
}

Complex& Complex::suma(const Complex& z){
	this->real +=  z.Re();
	this->imaginario += z.Im();
	return *this;
}

Complex& Complex::producto(const Complex& z){

	// por comodidad a this->imaginario lo llamo wi y a this->real lo llamo wr, me los guardo aparte sino anda todo mal ;)
	inFloat ri = this->real;
	ri *= z.Im();
	inFloat rr = this->real;
	rr *= z.Re();
	inFloat ir = this->imaginario;
	ir *= z.Re();
	inFloat ii = this->imaginario;
	ii *= z.Im();
	//inFloat wi = this->imaginario; 				// 1
	
	this->real = rr - ii;
	this->imaginario = ri + ir;
	
	//this->real = wr * z.Re() - wi * z.Im();			// 1
	//this->imaginario = wr * z.Im() + wi * z.Re();			// 1
	
	//si hago así y anda me evito la versión marcada con 1 que hace una copia local de this.imaginario y que puede ser graaande!
	// el wr original me parece que es insalvable porque se modifica en el primer this->real = ... lo dejo x si se les ocurre alog mejor
	return *this;
}

Complex& Complex::producto(const inFloat& lambda){
	this->real = lambda * this->real;
	this->imaginario = lambda * this->imaginario; 
	return *this;
}

Complex& Complex::cuadrado(){
	inFloat wr = this->real; // la parte real original, es horrible esta clase o hago todo mal :(
	inFloat realMenosImag = wr - this->imaginario;
	this->real += this->imaginario;
	this->real *= realMenosImag;// (this->real + this->imaginario) * (this->real - this->imaginario);
	this->imaginario *= 2; //hay q modificar esto para que ande NEED CHANGE
	this->imaginario *= wr;
	
	/* Sale de... (je, esto se llama tener el karma del álgebra encima)
	 sea z = (a+bi), entonces z^2 es:
		Re(z^2) = Re(z)^2 + Im(z)^2 = a.a - b.b ~ corregido por (a+b).(a-b) por ser más estable!
		Im(z^2) = 2 Re(z) Im(z) = 2.a.b
	*/
	return *this;
}

bool Complex::operator==(const Complex& z) const{
	return (this->real == z.Re()) && (this->imaginario == z.Im());

}

bool Complex::operator!=(const Complex& z) const{
	return !(*this == z);
}

inFloat Complex::moduloModulo() const{
	return this->real * this->real + this->imaginario * this->imaginario;
}

Complex& Complex::operator=(const Complex& z){
	if (this != &z){
		this->real = z.Re();
		this->imaginario = z.Im();
	}
	return *this;

}

Complex& Complex::setRe(inFloat r){
	this->real = r;
	return *this;
}

Complex& Complex::setIm(inFloat i){ // referenciaaaaaaaaaa :'(
	this->imaginario = i;
	return *this;
}


ostream& operator<<(ostream& out, const Complex& z) {
	out<<"("<< z.Re()<<"," << z.Im()<< ")";
	return out;
//coutea en forma de (Re,Im) :)
}

